package br.ifpb.edu.xmi.parser;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;

import br.ifpb.edu.uml.elements.AttributeSimp;
import br.ifpb.edu.uml.elements.Class;
import br.ifpb.edu.uml.elements.Methods;
import br.ifpb.edu.uml.elements.Relationship;

public class CollectInfo {
	
	private ArrayList<Class> classDiagram = new ArrayList<Class>();
	
	/**
	 * Este metodo ler todo o modelo de entrada e coleta todas as classes
	 * encontradas
	 * 
	 * @param file
	 *            Localizacao do modelo de entrada
	 * @return Uma lista das classes encontradas no modelo de entrada
	 */
	public void collectInfo(String file) {
		
		String lowerBound = new String( "0" );
		String upperBound = new String( "1" );
		String name = new String();
		String type = new String();

		try {

			FileReader fr = new FileReader(file);
			BufferedReader br = new BufferedReader(fr);

			String line = null;

			Class c = null;
			Relationship r = new Relationship();
			Methods m = new Methods();
			AttributeSimp as = new AttributeSimp();

			while ((line = br.readLine()) != null) {

				String[] v = line.split("\n");

				for (String dado : v) {
					
					// IDENTIFICA UMA CLASSE
					if (dado.lastIndexOf("<eClassifiers xsi:type=\"ecore:EClass\" name=\"") != -1) {
						c = addClass( dado, c );
					}
					
					// IDENTIFICA UM ATRIBUTO
					if (dado.lastIndexOf("<eStructuralFeatures xsi:type=\"ecore:EAttribute\" name=\"") != -1) {
						c = addAttribute( name, type, dado, c );
					}
					
					// IDENTIFICA UM MÉTODO
					if (dado.lastIndexOf("<eOperations name=\"") != -1) {
						c = addMethod( name, type, dado, m, c );
					}

					// IDENTIFICA PARAMETROS DE UM METODO
					if (dado.lastIndexOf("<eParameters name=\"") != -1) {
						c= addParameter( name, type, dado, as, m, c);
					}
					
					// IDENTIFICA RELACIONAMENTOS
					if (dado.lastIndexOf("<eStructuralFeatures xsi:type=\"ecore:EReference\" name=\"") != -1) {
						c = idRelationship( dado, name, lowerBound, type, r, c );
					}
					
					// ALTERA O TIPO DO RELACIONAMENTO
					if (dado.lastIndexOf("containment=\"") != -1) {
						c = changeTypeRelationship( r, c );
					}
					
					// IDENTIFICA CARACTERISTICAS EXTRAS DE UM RELACIONAMENTO
					if (dado.lastIndexOf("upperBound=\"") != -1) {
						c = idRelationships( dado, upperBound, lowerBound, type, c, r );
					}
					
				}
			}

		} catch (Exception e) {
			file = "Erro ao ler o arquivo..";
		}
		setList( classDiagram );
	}
		
		/**
		 * Adiciona um atributo na classe
		 * @param name Assinatura do atributo
		 * @param type Tipo da variavel
		 * @param data Linha do codigo do metamodelo
		 * @param c Classe que armazenara o atributo
		 */
		private Class addAttribute( String name, String type, String data, Class c ) {
			name = (data = data.substring(data
					.indexOf("name=\"") + 6)).substring(0,
					data.indexOf("\""));
			type = (data = data.substring(data
							.indexOf("#//") + 4)).substring(0,
							data.indexOf("\""));
			c.setAttribute("private", type, name);
			return c;
		}
		
		/**
		 * Este metodo adiciona um metodo a partir do que foi lido
		 * @param name Assinatura do metodo
		 * @param type Tipo de retorno do metodo
		 * @param data Linha do codigo do metamodelo
		 * @param m Metodo criado
		 * @param c Classe a ter o metodo adicionado
		 */
		private Class addMethod( String name, String type, String data, Methods m, Class c ) {
			name = (data = data.substring(data
					.indexOf("name=\"") + 6)).substring(0,
					data.indexOf("\""));
			m.setName(name);
			m.setAcessModified("default");
			m.setTypeReturn("void");
			if (data.indexOf("#//") != -1) {
				type = (data = data.substring(data
								.indexOf("#//") + 4)).substring(0,
								data.indexOf("\""));
				m.setTypeReturn(type);
			}
			c.setMethod(m);
			return c;
		}
		
		/**
		 * Adiciona parametro a um metodo
		 * @param name nome da variavel
		 * @param type tipo de retorno
		 * @param data linha do codigo
		 * @param attributeSimp Atributo simples
		 * @param m Metodo a ter informações lidas
		 * @param c Classe a ter metodo alterado
		 */
		private Class addParameter( String name, String type, String data, AttributeSimp attributeSimp, Methods m, Class c) {
					name = (data = data.substring(data
							.indexOf("name=\"") + 6)).substring(0,
							data.indexOf("\""));
					type = (data = data.substring(data
							.indexOf("#//") + 4)).substring(0,
							data.indexOf("\""));
			attributeSimp.setName( name );
			attributeSimp.setType( type );
			m = c.getMethods( c.sizeMethods()-1 );
			m.setAttributes( attributeSimp );
			c.removeMethod( );
			c.setMethod( m );
			return c;
		}
		
		/**
		 * Altera o tipo do relacionamento entre as classes
		 * @param relationship Relacionamento criado
		 * @param c Classe criada
		 */
		private Class changeTypeRelationship( Relationship relationship, Class c ) {
			relationship = c.getRelationships( c.sizeRelationships()-1 );
			relationship.setRelationshipType( "agregação" );
			c.removeRelationships( );
			c.setRelatioships( relationship );
			return c;
		}
		
		
		/**
		 * Identifica o Relacionamento existente em uma determinada classe
		 * @param data linha a ser estudada
		 * @param name nome da classe mãe
		 * @param lowerBound multiplicidade minima
		 * @param type tipo do relacionamento
		 * @param relationship relacionamento criado
		 * @param c classe existente
		 */
		private Class idRelationship( String data, String name, String lowerBound, String type, Relationship relationship, Class c ) {
			String auxiliar = data;
			name = (data = data.substring(data
							.indexOf("name=\"") + 6)).substring(0,
							data.indexOf("\""));
			if( auxiliar.lastIndexOf( "lowerBound=\"" ) != -1 )
				lowerBound = idLowerBound( data );
			if( auxiliar.lastIndexOf( "eType=\"#//" ) != -1 )
				type = idType( auxiliar );
			data = auxiliar;
			relationship.setMultiplicity( lowerBound+"..1" );
			relationship.setMotherClass( type );
			relationship.setRelationshipType( "Associação" );
			c.setRelatioships( relationship );
			return c;
		}
		
		/**
		 * Identifica a multiplicade minima na linha do metamodelo
		 * @param data linha do metamodelo
		 * @param lowerBound multiplicidade minha
		 * @return valor da multiplicidade
		 */
		private String idLowerBound( String data ) {
			return (( data = data.substring(data
					.indexOf("lowerBound=\"") + 12)).substring(0,
							data.indexOf("\"")));
		}
		
		/**
		 * Identifica o tipo do relacionamento
		 * @param auxiliary Linha a ser analisada
		 * @return Retorna o tipo do relacionamento
		 */
		private String idType( String auxiliary ) {
			String dado = auxiliary;
			return (( dado = dado.substring(dado
					.indexOf("eType=\"#//") + 11)).substring(0,
							dado.indexOf("\"")));
		}
		
		/**
		 * Identifica a multiplicade maxima em uma borda, bem como o tipo do relacionamento se existir
		 * @param data linha a ser analisada
		 * @param upperBound maior valor da multiplicidade
		 * @param lowerBound menor valor da multiplicidade
		 * @param type tipo do relacionamento
		 * @param c Classe a ter o relacionamento criado
		 * @param relationship Relacionamento criado entre as classes
		 */
	private Class idRelationships( String data, String upperBound, String lowerBound, String type, Class c, Relationship relationship ) {
		String auxiliar = data;
		upperBound = ( data = data.substring(data
				.indexOf("upperBound=\"") + 12)).substring(0,
						data.indexOf("\""));
		data = auxiliar;
		if( data.lastIndexOf( "eType=\"#//" ) != -1 )
			type = idType( auxiliar );
		data = auxiliar;
		relationship = c.getRelationships( c.sizeRelationships()-1 );
		relationship.setMultiplicity( lowerBound+".."+upperBound );
		relationship.setMotherClass( type );
		c.removeRelationships( );
		c.setRelatioships( relationship );
		return c;
	}
	
	/**
	 * Adiciona uma classe ao sistema
	 * @param data trecho de código a ser coletado as informações
	 * @param c Classe que será adicionada
	 */
	private Class addClass( String data, Class c ) {
		String auxiliar = data;
		data = (data = data.substring(data.indexOf("name=\"") + 6)).substring(0,data.indexOf("\""));
		c = new Class(data, true);
		if (c != null)
			classDiagram.add(c);
		if (auxiliar.lastIndexOf("eSuperTypes=\"#//") != -1)
			c = addInheritance( auxiliar, c );
		return c;
	}
	
	/**
	 * Adiciona um relacionamento de Herança na classe
	 * @param auxiliary trecho da linha a ser coletado as informações
	 * @param c Classe a ter o relacionamento adicionado
	 */
	private Class addInheritance( String auxiliary, Class c ) {
		auxiliary = (auxiliary = auxiliary.substring(auxiliary.indexOf("eSuperTypes=\"#//") + 16)).substring(0,auxiliary.indexOf("\""));
		c.setRelationship(auxiliary, "herança");
		return c;
	}
	
	/**
	 * Define a lista de classes do parser
	 * 
	 * @param list
	 *            Lista a ser definida
	 */
	private void setList(ArrayList<Class> list) {
		classDiagram = list;
	}
	

	/**
	 * Este metodo retorna uma lista de classes contidas no parser
	 * 
	 * @return Lista de Classes Modificadas
	 */
	public ArrayList<Class> getList() {
		return classDiagram;
	}

	
}
